001 package net.sf.xdc.util;
002
003 /*
004 * Copyright 2005-2006 Jens Voß.
005 *
006 * Licensed under the GNU Lesser General Public License (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://opensource.org/licenses/lgpl-license.php
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 import java.util.StringTokenizer;
020 import java.util.regex.Pattern;
021 import java.io.FilenameFilter;
022 import java.io.File;
023
024 /**
025 * This class implements the <code>FilenameFilter</code> interface and is used
026 * to filter out files from a list of files contained in a directory.
027 *
028 * @author Jens Voß
029 * @since 0.5
030 * @version 0.5
031 */
032 public class PathDescriptor implements FilenameFilter {
033
034 /**
035 * This utility method is used to convert an expression containing "basic"
036 * wildcards like "?" (any character), "*" (any sequence of characters
037 * except a path separator), "**" (any seqence of directory names contained
038 * in each other) to a regular expression.
039 *
040 * @param str The string to be converted to a regular expression
041 * @return A regular expression corresponding to the string with "basic"
042 * wildcards
043 */
044 public static String convert(String str) {
045 StringBuffer retVal = new StringBuffer();
046 char[] chars = str.toCharArray();
047 for (int i = 0; i < chars.length; i++) {
048 char c = chars[i];
049 if (c == '.') {
050 retVal.append("\\.");
051 }
052 else if (c == '+') {
053 retVal.append("\\+");
054 }
055 else if (c == '?') {
056 retVal.append('.');
057 }
058 else if (c == '*') {
059 if (i < chars.length - 1 && chars[i+1] == '*') {
060 retVal.append(".*");
061 i++;
062 }
063 else {
064 retVal.append("[^/]*");
065 }
066 }
067 else {
068 retVal.append(c);
069 }
070 }
071 return retVal.toString();
072 }
073
074 private String regex;
075 private boolean defaultExcludes;
076 private PathDescriptor childDescriptor;
077
078 private PathDescriptor(StringTokenizer tok, boolean defaultExcludes) {
079 this.defaultExcludes = defaultExcludes;
080 regex = convert(tok.nextToken());
081 if (tok.hasMoreTokens()) {
082 childDescriptor = new PathDescriptor(tok, defaultExcludes);
083 }
084 }
085
086 /**
087 * Public constructor.
088 *
089 * @param path A path pattern. Its constituents are delimited by forward
090 * slashes ('/') and may contain "basic" wildcards: An asterisk ("*")
091 * means "any substring" (even an empty substring); a question mark
092 * ("?") means "any character".
093 * @param defaultExcludes Specifies whether directories or files probably
094 * containing version control information should be excluded from
095 * processing
096 */
097 public PathDescriptor(String path, boolean defaultExcludes) {
098 this(new StringTokenizer(path, "/", false), defaultExcludes);
099 }
100
101 /**
102 * This is an implementation of the method defined in the
103 * <code>FilenameFilter</code> interface. It tests whether a file with a
104 * certain name is included in the list passing through this filter.
105 *
106 * @param dir The directory in which the file is found
107 * @param name The name of the file
108 * @return <b>true</b> if the file should be included in the list (in this
109 * case if and only if the file name matches the pattern of the first
110 * constituent of the path used for construction); <b>false</b>
111 * otherwise
112 */
113 public boolean accept(File dir, String name) {
114 File file = new File(dir, name);
115 return matches(file) || matchesDirWithWildcard(file);
116 }
117
118 /**
119 * This method determines whether a file is a directory and the path pattern
120 * of this <code>PathDescriptor</code> is the path wildcard pattern ("**").
121 *
122 * @param file The file to be tested
123 * @return <b>true</B> if this <code>PathDescriptor</code>'s path pattern is
124 * "**" and the file exists and is a directory and is not to be
125 * excluded because it might be a version control directory.
126 */
127 public boolean matchesDirWithWildcard(File file) {
128 if (!".*.*".equals(regex)
129 || (defaultExcludes && IOUtils.isDefaultExclude(file.getName()))) {
130 return false;
131 }
132 return file.exists() && file.isDirectory();
133 }
134
135 /**
136 * This method determines whether a String matches the pattern expressed by
137 * the first constituent of the path used for construction of this
138 * <code>PathDescriptor</code>.
139 *
140 * @param file The <code>File</code> whose name is to be compared to the
141 * pattern of this <code>PathDescriptor</code>
142 * @return <b>true</b> if the String matches the pattern; <b>false</b>
143 * otherwise
144 */
145 public boolean matches(File file) {
146 return !regex.equals(".*.*") && Pattern.matches(regex, file.getName());
147 }
148
149 /**
150 * If the path used for construction of this <code>PathDescriptor</code>
151 * consists of more than one constituent, this method can be used to retrieve
152 * a child descriptor corresponding to the sub-path after the first
153 * constituent.
154 *
155 * @return The sub-path after the first constituent of the path used for
156 * constructing this <code>PathDescriptor</code>. If that path only
157 * consisted of one such part, the method returns <b>null</b>.
158 */
159 public PathDescriptor getChildDescriptor() {
160 return childDescriptor;
161 }
162
163 }